import datetime

from django.db.models.functions import TruncDate
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema

from lab.common.base_model_view_set import BaseModelViewSet
from lab.common.enums import MessageTypeEnum
# Create your views here.
from lab.common.id_factory import id_factory
from lab.common.pagination import CustomPageNumberPagination
from lab.common.response import OK, BadRequest
from lab.device.models import Appointment, AppointmentMaterial, Lab, Device, STATUS_ACTIVE, Material
from lab.device.serializers import AppointmentSerializer, AppointmentWriteSerializer, \
    DeviceListSerializer, FileSerializer, LabListSerializer, MaterialSerializer, MyAppointmentSerializer
from lab.message.service import MessageService
from lab.utils import utils

APPOINTMENT_STATUS_DICT = {
    'pending': 0,
    'pass': 1,
    'reject': 2,
    'cancel': 3
}


# 预约空状态

class LabViewSet(BaseModelViewSet):
    """
    retrieve:
        获取实验室设备列表(0-维修 1-空闲 2-已约满)
    list:
        获取实验室列表(0-待审批，1-通过，2-拒绝，3-取消)
    """
    serializer_class = LabListSerializer

    def get_queryset(self):
        return Lab.objects.filter(status=STATUS_ACTIVE)

    @swagger_auto_schema(
        manual_parameters=[
            openapi.Parameter(
                'date',
                openapi.IN_QUERY,
                description="时间格式-'%Y-%m-%d'",
                type=openapi.TYPE_STRING
            )
        ]
    )
    def retrieve(self, request, id):
        try:
            _date = request.GET.get('date')
            if _date:
                date_param = datetime.datetime.strptime(_date, '%Y-%m-%d').date()
            else:
                date_param = datetime.datetime.today()
        except ValueError:
            return BadRequest(data={'error': 'Invalid date format. Use YYYY-MM-DD.'})
        devices = Device.objects.filter(lab_id=id)
        appointments = Appointment.objects.annotate(date_only=TruncDate('appointment_date')).filter(
            date_only=date_param).filter(device_id__in=[device.id for device in devices]).all()
        device_occupied_time_slot_dict = {device.id: '000000000000000000000000' for device in devices}
        for appointment in appointments:
            device_occupied_time_slot_dict[appointment.device_id] = utils.or_calc(
                device_occupied_time_slot_dict[appointment.device_id], appointment.occupied_time_slot)
        _devices = DeviceListSerializer(devices, many=True).data
        for _device in _devices:
            _device['occupied_time_slot'] = device_occupied_time_slot_dict.get(_device['id'],
                                                                               '000000000000000000000000')
        return OK(data=_devices)

    def list(self, request, *args, **kwargs):
        user_id = request.user.id if request.user.id else 1
        appointments = Appointment.objects.filter(user_id=user_id,
                                                  status=APPOINTMENT_STATUS_DICT.get('pending')).order_by(
            '-created_time')[:5]
        labs = self.get_queryset()
        response = {
            'my_appointments': {'count': len(appointments),
                                'appointments': MyAppointmentSerializer(appointments, many=True).data},
            'labs': LabListSerializer(labs, many=True).data
        }
        return OK(data=response)


class AppointmentViewSet(BaseModelViewSet):
    """
    retrieve:
        获取预约明细
    create:
        创建预约
    list:
        获取我的预约列表
    update:
        更新预约
    """
    queryset = Appointment.objects.all()
    serializer_class = AppointmentSerializer
    write_serializer_class = AppointmentWriteSerializer
    pagination_class = CustomPageNumberPagination
    lookup_field = 'id'

    def create(self, request, *args, **kwargs):
        # Extract nested materials data
        materials = request.data.pop('materials', [])
        request.data['order_number'] = id_factory.get_unique_id()
        # here we need to get the correct user id when we add auth related
        request.data['user_id'] = 1
        # Create the appointment instance
        appointment = Appointment.objects.create(**request.data)
        # create an appointment message
        MessageService.add_message(appointment.id, MessageTypeEnum.APPOINTMENT, "", request.data['user_id'])
        for material in materials:
            AppointmentMaterial.objects.create(appointment_id=appointment.id, material_id=material.get('material_id'), number=material.get('number'))
        response = AppointmentSerializer(appointment).data
        return OK(data=response)

    @swagger_auto_schema(
        manual_parameters=[
            openapi.Parameter(
                'status',
                openapi.IN_QUERY,
                description="预约状态-pending,pass,reject,cancel",
                type=openapi.TYPE_STRING
            )
        ]
    )
    def list(self, request, *args, **kwargs):
        user_id = request.user.id if request.user.id else 1
        status = APPOINTMENT_STATUS_DICT.get(request.GET.get('status'))
        appointments = Appointment.objects.filter(user_id=user_id)
        if status:
            appointments = appointments.filter(status=status)
        queryset = self.paginate_queryset(appointments.all())
        return self.get_paginated_response(MyAppointmentSerializer(queryset, many=True).data)

    def update(self, request, id, *args, **kwargs):
        super().update(request, *args, **kwargs)
        materials = request.data.pop('materials', [])
        if materials:
            AppointmentMaterial.objects.filter(appointment_id=id).delete()
            for material in materials:
                AppointmentMaterial.objects.create(appointment_id=id, **material)
        response = AppointmentSerializer(self.get_object()).data
        return OK(data=response)
    
class AppointmentCancelViewSet(BaseModelViewSet):
    """
    update:
        取消预约
    """
    queryset = Appointment.objects.all()
    lookup_field='id'
    def update(self, request, id, *args, **kwargs):
        appointment = Appointment.objects.filter(id=id).first()
        if appointment:
            appointment.status = APPOINTMENT_STATUS_DICT.get('cancel')
            appointment.save()
            response = AppointmentSerializer(self.get_object()).data
            return OK(data=response)
        else:
            return NotFound()
    
class MaterialViewSet(BaseModelViewSet):
    """
    list:
        获取耗材列表
    """
    serializer_class = MaterialSerializer
    queryset = Material.objects.all()


class FileViewSet(BaseModelViewSet):
    """
    create:
        上传文件
    """
    serializer_class = FileSerializer
